
export const DefaultPromise = <T>(t:T) => new Promise<T>((resolve) => resolve(t));

export type ClassContructor<T> = new (...args: any[]) => T;

export class Optional<T> {
    value: T|null|undefined
    constructor(value: T|null|undefined) {
        this.value = value
    }

    static empty<T>(): Optional<T> {
        return new Optional<T>(undefined)
    }

    isPresent(): boolean {
        return this.value != null && this.value != undefined
    }

    get(): T {
        return this.value as T
    }
}


/**
 * @title IEventListener
 * @author wzdong
 * @description 事件监听器, 用于需要监听自定义事件的类继承或挂载其成员方法 on\off\emit , 或需要监听自定义事件的对象挂载 on\off\emit 方法
 *
 * @eg
 * class ChildClass extends IEventListener<EvtName, Evt> {
 *     constructor() {
 *         super(evtNameList?: EvtName[])
 *     }
 * }
 */
class IEventListener<EvtName extends string, Evt = any> {
    private _evtNameList?: EvtName[];
    private _evtHooksRecord: Record<EvtName, Array<(evt: Evt) => void>> = {} as any;

    constructor(evtNameList?: EvtName[]) {
        this._evtNameList = evtNameList;
    }
    private _findEvtHooks(
        evtName: keyof typeof this._evtHooksRecord
    ): Record<EvtName, ((evt: Evt) => void)[]>[EvtName] | never {
        if (this._evtNameList && !this._evtNameList.includes(evtName))
            throw new Error(`There is no event called ${evtName}!`);
        this._evtHooksRecord[evtName] = this._evtHooksRecord[evtName] ?? [];
        return this._evtHooksRecord[evtName];
    }
    emit(evtName: keyof typeof this._evtHooksRecord, evt: Evt, self?: object) {
        const evtHooks = this._findEvtHooks(evtName);
        evtHooks.forEach((cbk) => cbk.bind(self)(evt));
    }
    // 添加事件，类似于HTMLElement.addEventListener
    on(evtName: keyof typeof this._evtHooksRecord, listener: (evt: Evt) => void) {
        const evtHooks = this._findEvtHooks(evtName);
        evtHooks.push(listener);
    }
    // 注销事件，类似于HTMLElement.removeEventListener
    off(evtName: keyof typeof this._evtHooksRecord, listener: (evt: Evt) => void) {
        const evtHooks = this._findEvtHooks(evtName);
        evtHooks.splice(evtHooks.indexOf(listener), 1);
    }
}

/**
 * @eg1
 * class someClass {
 *     constructor() {
 *         useEventListener.bind(this)<EvtName, Evt>(evtNameList?: EvtName[])
 *     }
 * }
 * @eg2
 * const someObj = {}
 * useEventListener.bind(someObj)<EvtName, Evt>(evtNameList?: EvtName[])
 */
export function useEventListener<EvtName extends string, Evt = any>(this: any,evtNameList?: EvtName[]) {
    const evtListener = new IEventListener<EvtName, Evt>(evtNameList);
    this.on = evtListener.on.bind(evtListener);
    this.off = evtListener.off.bind(evtListener);
    this.emit = (evtName: EvtName, evt: Evt) => evtListener.emit.bind(evtListener)(evtName, evt, this);
}


